Skip to content

route: support v4-mapped IPv6 nexthops on V6 routes - #1198

Open
fdomain wants to merge 1 commit into
vishvananda:mainfrom
fdomain:route-v4mapped-v6-gateway
Open

route: support v4-mapped IPv6 nexthops on V6 routes#1198
fdomain wants to merge 1 commit into
vishvananda:mainfrom
fdomain:route-v4mapped-v6-gateway

Conversation

@fdomain

@fdomain fdomain commented Jul 6, 2026

Copy link
Copy Markdown

This is an alternative proposal to fix #1114. The initial plan was to migrate to netip #1181, but considering the size of the change, we believe it would be more acceptable to make minor fixes to handle IPv4-mapped IPv6 address as gw.

Change:

A v4-mapped IPv6 address (::ffff:a.b.c.d) is byte-identical to its IPv4 form as a net.IP, so nl.GetIPFamily reports FAMILY_V4 for it. When such an address was used as a route's gateway on a V6 route it was encoded as a 4-byte AF_INET nexthop, and combined with a V6 destination it failed outright with "gateway... not the same IP family".

Resolve this locally in prepareRouteReq rather than changing GetIPFamily (which has no route context and must keep reporting V4 for the common net.ParseIP("1.2.3.4") case used by many other callers):

  • Honor route.Family, which was previously ignored on the add path, so a gateway-only route (no destination to pin the family) can still be requested as V6.
  • When the caller explicitly requests a V6 route via route.Family, let a 16-byte v4-mapped gateway (route.Gw and multipath nexthops) conform to it and be encoded as a 16-byte AF_INET6 nexthop.

Support is opt-in via route.Family so existing callers see no change: a v4-mapped gateway without route.Family set is still treated as V4 and rejected on a V6 route, exactly as before. The conform is also gated on a 16-byte slice so that an explicit 4-byte IPv4 gateway still errors rather than being silently reinterpreted; a 4-byte slice is the only unambiguous "I meant IPv4" signal available at the net.IP level.

Scope is limited to the gateway/nexthop, the real-world case (verified against the kernel: ip -6 route reports the nexthop as ::ffff:a.b.c.d). v4-mapped addresses are a socket-API representation construct and are not routable as IPv6 destinations, so Dst and Src are left unchanged.

Fixes #1114

Summary by CodeRabbit

  • Bug Fixes
    • Fixed route requests so v4-mapped IPv6 gateways are handled correctly when an IPv6 route family is explicitly requested.
    • Tightened validation to reject inconsistent gateway/address-family combinations instead of producing incorrect or ambiguous results.
    • Confirmed standard IPv4 gateway behavior remains unchanged.
  • Tests
    • Added coverage to verify correct gateway encoding and error cases for both single-path and multipath routes.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 731a444a-7ba5-463f-9b78-dd7a305e8d5d

📥 Commits

Reviewing files that changed from the base of the PR and between b275745 and 03feb9f.

📒 Files selected for processing (2)
  • route_linux.go
  • route_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • route_test.go

📝 Walkthrough

Walkthrough

prepareRouteReq now respects an explicitly set route.Family and handles v4-mapped IPv6 gateways as IPv6 only when FAMILY_V6 is requested. The tests add coverage for single-route and multipath gateway encoding, plus family-mismatch cases.

Changes

V4-mapped IPv6 gateway support

Layer / File(s) Summary
Explicit family and gateway conformance
route_linux.go
prepareRouteReq seeds the encoding family from route.Family when non-zero, and conforms v4-mapped IPv6 gateways for both route.Gw and multipath nh.Gw only when route.Family == FAMILY_V6 is explicitly set.
Gateway encoding tests
route_test.go
Adds a helper and tests verifying explicit-family IPv6 encoding, multipath gateway encoding, missing-family rejection, plain IPv4 behavior, and rejection of a 4-byte gateway on an IPv6 route.

Estimated code review effort: 2 (Simple) | ~15 minutes

Suggested reviewers: borkmann

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: supporting v4-mapped IPv6 nexthops on IPv6 routes.
Linked Issues check ✅ Passed The changes implement #1114 by allowing explicit IPv6 routes to accept v4-mapped gateways while preserving ordinary IPv4 behavior.
Out of Scope Changes check ✅ Passed The additions stay focused on route and gateway family handling plus targeted tests, with no obvious unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
route_linux.go (1)

993-1001: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated v4-mapped conform logic between single-Gw and multipath nh.Gw paths.

The exact same conform condition/comment is repeated for route.Gw (Lines 993-1001) and nh.Gw (Lines 1036-1044). Extracting a small helper would keep the two paths from silently diverging if this logic is tweaked later.

♻️ Proposed refactor
+// conformV4MappedGatewayFamily conforms a v4-mapped IPv6 gateway
+// (::ffff:a.b.c.d, 16 bytes) to FAMILY_V6 when the caller explicitly
+// requested a V6 route via route.Family. This is opt-in rather than
+// silently reinterpreting an ambiguous address; a 4-byte IPv4 gateway
+// is left to error downstream.
+func conformV4MappedGatewayFamily(routeFamily int, gw net.IP, gwFamily int) int {
+	if routeFamily == FAMILY_V6 && gwFamily == FAMILY_V4 && len(gw) == net.IPv6len {
+		return FAMILY_V6
+	}
+	return gwFamily
+}
+
 	if route.Gw != nil {
 		gwFamily := nl.GetIPFamily(route.Gw)
-		if route.Family == FAMILY_V6 && gwFamily == FAMILY_V4 && len(route.Gw) == net.IPv6len {
-			gwFamily = FAMILY_V6
-		}
+		gwFamily = conformV4MappedGatewayFamily(route.Family, route.Gw, gwFamily)
 ...
 			if nh.Gw != nil {
 				gwFamily := nl.GetIPFamily(nh.Gw)
-				if route.Family == FAMILY_V6 && gwFamily == FAMILY_V4 && len(nh.Gw) == net.IPv6len {
-					gwFamily = FAMILY_V6
-				}
+				gwFamily = conformV4MappedGatewayFamily(route.Family, nh.Gw, gwFamily)

Also applies to: 1036-1044

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@route_linux.go` around lines 993 - 1001, The v4-mapped IPv6 gateway
conforming logic is duplicated in both the single-gateway and multipath nexthop
paths, which risks the two branches drifting apart. Extract the shared
FAMILY_V6/FAMILY_V4 + net.IPv6len check into a small helper and call it from
both the route.Gw handling and the nh.Gw handling so the behavior stays
identical in both places.
route_test.go (1)

2842-2890: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No test exercises the multipath (nh.Gw) conform path.

All four new tests cover only route.Gw. The PR/AI summary states the v4-mapped conform is applied to route.MultiPath nexthops as well (route_linux.go Lines 1036-1044), but that duplicated logic is currently untested — a regression there (e.g. someone updates one conform block but not the other) would go unnoticed.

Consider adding a case using Route{Family: FAMILY_V6, MultiPath: []*NexthopInfo{{Gw: gw, LinkIndex: ...}}} and asserting the nested RTA_MULTIPATH attribute's child RTA_GATEWAY is 16 bytes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@route_test.go` around lines 2842 - 2890, The current tests only cover the
direct Route.Gw path, so the multipath nexthop branch in prepareRouteReq remains
untested. Add a test in TestPrepareRouteReqV4MappedV6Gateway (or a sibling test)
that builds a Route with Family set to FAMILY_V6 and MultiPath containing a
NexthopInfo with Gw set to the v4-mapped IP, then verify the nested
RTA_MULTIPATH child RTA_GATEWAY is encoded as 16 bytes. Use the existing
prepareRouteReq, Route, and NexthopInfo symbols to locate the multipath conform
logic and ensure it matches the direct gateway behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@route_linux.go`:
- Around line 993-1001: The v4-mapped IPv6 gateway conforming logic is
duplicated in both the single-gateway and multipath nexthop paths, which risks
the two branches drifting apart. Extract the shared FAMILY_V6/FAMILY_V4 +
net.IPv6len check into a small helper and call it from both the route.Gw
handling and the nh.Gw handling so the behavior stays identical in both places.

In `@route_test.go`:
- Around line 2842-2890: The current tests only cover the direct Route.Gw path,
so the multipath nexthop branch in prepareRouteReq remains untested. Add a test
in TestPrepareRouteReqV4MappedV6Gateway (or a sibling test) that builds a Route
with Family set to FAMILY_V6 and MultiPath containing a NexthopInfo with Gw set
to the v4-mapped IP, then verify the nested RTA_MULTIPATH child RTA_GATEWAY is
encoded as 16 bytes. Use the existing prepareRouteReq, Route, and NexthopInfo
symbols to locate the multipath conform logic and ensure it matches the direct
gateway behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 418d0262-d92f-4524-87f4-e070abc8c347

📥 Commits

Reviewing files that changed from the base of the PR and between 4e35dc9 and b275745.

📒 Files selected for processing (2)
  • route_linux.go
  • route_test.go

@fdomain fdomain mentioned this pull request Jul 6, 2026
A v4-mapped IPv6 address (::ffff:a.b.c.d) is byte-identical to its IPv4
form as a net.IP, so nl.GetIPFamily reports FAMILY_V4 for it. When such
an address was used as a route's gateway on a V6 route it was encoded as
a 4-byte AF_INET nexthop, and combined with a V6 destination it failed
outright with "gateway... not the same IP family".

Resolve this locally in prepareRouteReq rather than changing
GetIPFamily (which has no route context and must keep reporting V4 for
the common net.ParseIP("1.2.3.4") case used by many other callers):

- Honor route.Family, which was previously ignored on the add path, so
  a gateway-only route (no destination to pin the family) can still be
  requested as V6.
- When the caller explicitly requests a V6 route via route.Family, let a
  16-byte v4-mapped gateway (route.Gw and multipath nexthops) conform to
  it and be encoded as a 16-byte AF_INET6 nexthop.

Support is opt-in via route.Family so existing callers see no change: a
v4-mapped gateway without route.Family set is still treated as V4 and
rejected on a V6 route, exactly as before. The conform is also gated on
a 16-byte slice so that an explicit 4-byte IPv4 gateway still errors
rather than being silently reinterpreted; a 4-byte slice is the only
unambiguous "I meant IPv4" signal available at the net.IP level.

Scope is limited to the gateway/nexthop, the real-world case (verified
against the kernel: `ip -6 route` reports the nexthop as ::ffff:a.b.c.d).
v4-mapped addresses are a socket-API representation construct and are
not routable as IPv6 destinations, so Dst and Src are left unchanged.

Fixes vishvananda#1114

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@fdomain
fdomain force-pushed the route-v4mapped-v6-gateway branch from b275745 to 03feb9f Compare July 7, 2026 07:01
@fdomain

fdomain commented Jul 9, 2026

Copy link
Copy Markdown
Author

Hi @vishvananda @aboch ,

This PR is ready for review, but the build is failing due to a seemingly unrelated issue. The same failure is visible on the following PR.

Thanks in advance for taking a look at my change whenever you have time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IPv4-mapped IPv6 address cannot be used as route gateway

1 participant